﻿namespace Jsl.FxCop
{
    using System;
    using System.Linq;
    using System.Windows.Forms;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Microsoft.FxCop.Sdk;

    [TestClass]
    public class When_getting_a_TypeNode_from_a_Type
    {
        [TestMethod]
        public void Can_get_the_correct_type()
        {
            var typeNode = typeof(When_getting_a_TypeNode_from_a_Type).GetTypeNode();

            AssertEx.That(() => typeNode.FullName == typeof(When_getting_a_TypeNode_from_a_Type).FullName);
        }

        [TestMethod]
        public void The_same_TypeNode_is_returned()
        {
            var typeNode1 = typeof(When_getting_a_TypeNode_from_a_Type).GetTypeNode();
            var typeNode2 = typeof(When_getting_a_TypeNode_from_a_Type).GetTypeNode();

            AssertEx.That(() => object.ReferenceEquals(typeNode1, typeNode2));
        }
    }

    [TestClass]
    public class When_getting_the_common_type
    {
        [TestMethod]
        public void When_the_two_classes_are_the_same_then_that_class_is_returned()
        {
            var typeA = typeof(A).GetClassNode();
            var commonType = typeA.GetCommonClass(typeA);

            AssertEx.That(() => commonType.FullName == "Jsl.FxCop.A");
        }

        [TestMethod]
        public void When_the_two_classes_are_the_same_derived_class_then_that_class_is_returned()
        {
            var derivedFromA = typeof(DerivedFromA).GetTypeNode();
            var commonType = derivedFromA.GetCommonClass(derivedFromA);

            AssertEx.That(() => commonType.FullName == "Jsl.FxCop.DerivedFromA");
        }

        [TestMethod]
        public void When_two_classes_do_not_share_a_base_then_typeof_object_is_returned()
        {
            var commonType = typeof(A).GetTypeNode().GetCommonClass(typeof(B).GetTypeNode());

            AssertEx.That(() => commonType == FrameworkTypes.Object);
        }

        [TestMethod]
        public void When_first_class_is_a_base_class_then_return_the_first_class()
        {
            var derivedFromA = typeof(DerivedFromA).GetTypeNode().DeclaringModule.ContainingAssembly.UniqueKey;
            var a = typeof(A).GetTypeNode().DeclaringModule.ContainingAssembly.UniqueKey;

            var commonType = typeof(DerivedFromA).GetTypeNode()
                .GetCommonClass(typeof(A).GetTypeNode());

            AssertEx.That(() => commonType.FullName == "Jsl.FxCop.A");
        }

        [TestMethod]
        public void When_second_class_is_a_base_class_then_return_the_second_class()
        {
            var commonType = typeof(A).GetTypeNode()
                .GetCommonClass(typeof(DerivedFromA).GetTypeNode());

            AssertEx.That(() => commonType.FullName == "Jsl.FxCop.A");
        }

        [TestMethod]
        public void When_two_classes_derive_from_same_base_return_base()
        {
            var commonType = typeof(DerivedFromA).GetTypeNode()
                .GetCommonClass(typeof(DerivedFromA2).GetTypeNode());

            AssertEx.That(() => commonType.FullName == "Jsl.FxCop.A");
        }
    }

    internal class A { }
    internal class DerivedFromA : A { }
    internal class DerivedFromA2 : A { }
    internal class B { }

    [TestClass]
    public class When_getting_a_common_interface
    {
        [TestMethod]
        public void When_the_two_interfaces_are_the_same_that_interface_is_returned()
        {
            var interfaceA = typeof(InterfaceA).GetInterfaceNode();
            var commonInterfaces = interfaceA.GetCommonInterfaces(interfaceA);

            AssertEx.That(() => commonInterfaces.Single().FullName == "Jsl.FxCop.InterfaceA");
        }

        [TestMethod]
        public void When_the_two_interfaces_are_the_same_derived_interface_then_that_interface_is_returned()
        {
            var interfaceFromA = typeof(InterfaceFromA).GetInterfaceNode();
            var commonInterfaces = interfaceFromA.GetCommonInterfaces(interfaceFromA);

            AssertEx.That(() => commonInterfaces.Single().FullName == "Jsl.FxCop.InterfaceFromA");
        }

        [TestMethod]
        public void When_two_interfaces_do_not_share_a_base_then_empty_collection_is_returned()
        {
            var interfaceA = typeof(InterfaceA).GetInterfaceNode();
            var interfaceB = typeof(InterfaceB).GetInterfaceNode();
            var commonInterfaces = interfaceA.GetCommonInterfaces(interfaceB);

            AssertEx.That(() => !commonInterfaces.Any());
        }

        [TestMethod]
        public void When_both_interfaces_only_share_single_interface_then_that_inteface_is_returned()
        {
            var interfaceFromA = typeof(InterfaceFromA).GetInterfaceNode();
            var interfaceFromAandB = typeof(InterfaceFromAandB).GetInterfaceNode();
            var commonInterfaces = interfaceFromA.GetCommonInterfaces(interfaceFromAandB);

            AssertEx.That(() => commonInterfaces.Single().FullName == "Jsl.FxCop.InterfaceA");
        }
    }

    internal interface InterfaceA { }
    internal interface InterfaceFromA : InterfaceA { }
    internal interface InterfaceB { }
    internal interface InterfaceFromAandB : InterfaceA, InterfaceB { }

    [TestClass]
    public class When_determining_synonymous_types
    {
        [TestMethod]
        public void The_exact_same_type_is_always_synonymous()
        {
            var controlType = typeof(Control).GetTypeNode();

            AssertEx.That(() => controlType.GetBestSynonymousType(controlType) == controlType);
        }

        [TestMethod]
        public void Different_non_simple_types_are_not_sysonymous()
        {
            var controlType = typeof(Control).GetTypeNode();
            var formType = typeof(Form).GetTypeNode();

            AssertEx.That(() => controlType.GetBestSynonymousType(formType) == null);
            AssertEx.That(() => formType.GetBestSynonymousType(controlType) == null);
        }

        [TestMethod]
        public void Boolean_wins_over_Int32()
        {
            var booleanType = typeof(bool).GetTypeNode();
            var int32Type = typeof(int).GetTypeNode();

            AssertEx.That(() => booleanType.GetBestSynonymousType(int32Type) == booleanType);
            AssertEx.That(() => int32Type.GetBestSynonymousType(booleanType) == booleanType);
        }

        [TestMethod]
        public void Enumerations_wins_over_ValueType()
        {
            var dialogResultType = typeof(DialogResult).GetTypeNode();
            var valueTypeType = typeof(ValueType).GetTypeNode();

            AssertEx.That(() => dialogResultType.GetBestSynonymousType(valueTypeType) == dialogResultType);
            AssertEx.That(() => valueTypeType.GetBestSynonymousType(dialogResultType) == dialogResultType);
        }
    }
}